//
// $Id: AlternateMovesHelper.m 8 2010-08-05 21:14:28Z principiadriver $
//
// Written by
//  Michael Klein <michael.klein@puffin.lb.shuttle.de>
//
// This file is part of Alternate Moves.
//
// This  is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#import "AlternateMovesHelper.h"
#import "BezelView.h"
#import "OverlayWindow.h"

#define kDomainName @"de.shuttle.lb.puffin.AlternateMoves"


static CGEventRef mouseEventCallback(CGEventTapProxy proxy,
                                     CGEventType type,
                                     CGEventRef event,
                                     void *refcon)
{
    AlternateMovesHelper *zelf = refcon;
    return [zelf mouseEventWithProxy:proxy type:type event:event];
}



@implementation AlternateMovesHelper

- (void)awakeFromNib
{
    bundle = [NSBundle mainBundle];

    NSString *path = [bundle pathForResource:@"Defaults" ofType:@"plist"];
    NSDictionary *defaults = [NSDictionary dictionaryWithContentsOfFile:path];
    [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
    [[NSUserDefaults standardUserDefaults] addSuiteNamed:kDomainName];

    buttonDownTap = CGEventTapCreate(kCGSessionEventTap,
                                     kCGHeadInsertEventTap,
                                     0,
                                     CGEventMaskBit(kCGEventLeftMouseDown),
                                     mouseEventCallback,
                                     self);

    buttonUpTap = CGEventTapCreate(kCGSessionEventTap,
                                   kCGHeadInsertEventTap,
                                   0,
                                   CGEventMaskBit(kCGEventMouseMoved) |
                                   CGEventMaskBit(kCGEventLeftMouseUp) |
                                   CGEventMaskBit(kCGEventLeftMouseDragged),
                                   mouseEventCallback,
                                   self);

    CGEventTapEnable(buttonUpTap, false);

    if (!(buttonUpTap && buttonUpTap))
        NSLog(@"failed to create event taps!");
    else
    {
        NSRunLoop *crl = [NSRunLoop currentRunLoop];
        [crl addPort:(NSPort*)buttonDownTap forMode:NSDefaultRunLoopMode];
        [crl addPort:(NSPort*)buttonUpTap forMode:NSDefaultRunLoopMode];
    }

    [NSThread detachNewThreadSelector:@selector(remoteAccessServer:)
                             toTarget:self
                           withObject:nil];
}


- (NSBundle*)loadEngine:(NSString*)name;
{
    NSString *path = [[bundle builtInPlugInsPath] stringByAppendingPathComponent:name];
    return [NSBundle bundleWithPath:path];
}


- (void)remoteAccessServer:(id)object
{
    NSAutoreleasePool *arp = [[NSAutoreleasePool alloc] init];
    NSConnection *dc = [NSConnection defaultConnection];
    [dc setRootObject:[NSProtocolChecker protocolCheckerWithTarget:self protocol:@protocol(AlternateMovesRemoteProtocol)]];
    [dc registerName:kDomainName];
    [[NSRunLoop currentRunLoop] run];
    [arp release];
}


- (CGEventRef)mouseEventWithProxy:(CGEventTapProxy)proxy
                             type:(CGEventType)type
                            event:(CGEventRef)event
{
    CGEventFlags flags;

    switch(type)
    {
        case kCGEventLeftMouseDown:
            flags = CGEventGetFlags(event);
            initialLoc = lastLoc = CGEventGetLocation(event);
            if (flags & kCGEventFlagMaskAlternate) {
                resizing = (flags & kCGEventFlagMaskCommand) != 0;
                if (axEngine == nil) {
                    axEngine = [self loadEngine:@"AXEngine.bundle"];
                }
                windowHandler = [[axEngine principalClass]
                                 newWindowHandlerForWindowAtPoint:initialLoc];
                if (windowHandler == nil &&
                    [[NSUserDefaults standardUserDefaults] boolForKey:@"EnableX11"]) {
                    if (x11Engine == nil) {
                        x11Engine = [self loadEngine:@"X11Engine.bundle"];
                    }
                    windowHandler = [[x11Engine principalClass] newWindowHandlerForWindowAtPoint:initialLoc];
                }
                if (windowHandler != nil) {
                    initialRect = nextRect = [windowHandler windowRect];
                    NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
                    [self updateWindowRect];

                    BezelView *bv = [infoWindow contentView];
                    NSData *data;
                    NSColor *color;

                    if ((data = [defs objectForKey:@"InfoWindowBackgroundColor"]) != nil) {
                        color = [NSUnarchiver unarchiveObjectWithData:data];
                        [bv setBackgroundColor:color];
                    }

                    /*
                    if ((data = [defs objectForKey:@"InfoWindowBorderColor"]) != nil) {
                        color = [NSUnarchiver unarchiveObjectWithData:data];
                        [bv setBorderColor:color];
                    }
                     */
                    if ((data = [defs objectForKey:@"InfoWindowTextColor"]) != nil) {
                        color = [NSUnarchiver unarchiveObjectWithData:data];
                        [infoWindowText setTextColor:color];
                    }

                    [infoWindow setAlphaValue:[defs floatForKey:@"InfoWindowOpacity"]];
                    [infoWindow orderFront:self];

                    CGEventTapEnable(buttonDownTap, false);
                    CGEventTapEnable(buttonUpTap, true);

                    return NULL;
                }
            }
            break;

        case kCGEventLeftMouseUp:
            if (windowHandler) {
                [timer fire];
                [windowHandler release];
                windowHandler = nil;

                [infoWindow fade:self];

                CGEventTapEnable(buttonUpTap, false);
                CGEventTapEnable(buttonDownTap, true);

                return NULL;
            }
            break;

        case kCGEventMouseMoved:
            if (windowHandler)
                return NULL;
            break;

        case kCGEventLeftMouseDragged:
            if (windowHandler) {
                lastLoc = CGEventGetLocation(event);
                float delay;
                if (resizing) {
                    nextRect.size = CGSizeMake(initialRect.size.width + lastLoc.x - initialLoc.x,
                                               initialRect.size.height + lastLoc.y - initialLoc.y);
                    delay = [[NSUserDefaults standardUserDefaults] floatForKey:@"ResizeDelay"];
                } else {
                    nextRect.origin = CGPointMake(initialRect.origin.x + lastLoc.x - initialLoc.x,
                                                  initialRect.origin.y + lastLoc.y - initialLoc.y);
                    delay = [[NSUserDefaults standardUserDefaults] floatForKey:@"MoveDelay"];
                }

                if (delay <= 0.0)
                    [self updateWindowRect];
                else
                    [self setupTimer:delay];
                return NULL;
            }
            break;
    }

    return event;
}


- (void)dealloc
{
    if (buttonDownTap)
        CFRelease(buttonDownTap);
    if (buttonUpTap)
        CFRelease(buttonUpTap);
    [super dealloc];
}


- (void)timerFired:(NSTimer*)t
{
    [self updateWindowRect];
    timer = nil;
}


- (void)setupTimer:(double)delay
{
    if (!timer)
    {
        timer = [NSTimer scheduledTimerWithTimeInterval:delay
                                                 target:self
                                               selector:@selector(timerFired:)
                                               userInfo:nil
                                                repeats:NO];
    }
}


- (void)updateWindowRect
{
    NSString *str;
    CGRect rect = [windowHandler setWindowRect:nextRect];
    if (resizing) {
        str = [NSString stringWithFormat:@"%.0fx%.0f", rect.size.width, rect.size.height];
    } else {
        str = [NSString stringWithFormat:@"%+.0f%+.0f", rect.origin.x, rect.origin.y];
    }

#if 0
    int i;
    NSRect r = NSZeroRect;
    NSArray *screens = [NSScreen screens];
    for (i = 0; i < [screens count]; i++) {
        r = NSUnionRect([[screens objectAtIndex:i] frame], r);
    }
#endif

    //NSPoint topLeft;
    //[infoWindow setFrameTopLeftPoint:NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height)];
    //[infoWindow setFrameTopLeftPoint:NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height)];
    //[infoWindow setFrameTopLeftPoint:NSMakePoint(rect.origin.x, r.origin.y + r.size.height - rect.origin.y)];
    //[infoWindow setFrameTopLeftPoint:NSMakePoint(lastLoc.x, r.origin.y + r.size.height - lastLoc.y)];
    [infoWindowText setStringValue:str];
}


- (NSString*)bundleNameAndVersion
{
    NSDictionary *dict = [bundle infoDictionary];
    return [NSString stringWithFormat:@"%@ %@",
            [dict objectForKey:@"CFBundleName"],
            [dict objectForKey:@"CFBundleVersion"]];
}

- (void)synchronizePreferences
{
    [[NSUserDefaults standardUserDefaults] synchronize];
}


- (void)terminate
{
    NSLog(@"terminate");
    [NSApp terminate:self];
}


@end

